// Dynamic tree editor - tree-like data structures editor for .NET/Mono.
// Copyright (C) 2006  Vladimir Sibirov
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

using System;
using System.Data;
using System.Collections;


namespace TreeEditor
{
	/// <summary>
	/// Specifies the fields a node of a distinct class contains. Every tree has a variety of node classes the node entities belong to.
	/// This class is responsible for creation/deletion/modification of node classes in the data model.
	/// </summary>
	public class Class
	{
		/// <summary>
		/// Unique class name.
		/// </summary>
		private string name;

		/// <summary>
		/// Identifier in the data model.
		/// </summary>
		private uint id;

		/// <summary>
		/// Field definition as name => type.
		/// </summary>
		private SortedList columns;

		/// <summary>
		/// The tree this class belongs to.
		/// </summary>
		private Tree tree;

		/// <summary>
		/// Dataset link.
		/// </summary>
		private DataSet data;

		/// <summary>
		/// Whether the class has been renamed.
		/// </summary>
		private bool renamed;
		
		/// <summary>
		/// Used for proper renaming.
		/// </summary>
		private string oldName;

		/// <summary>
		/// New node class constructor.
		/// </summary>
		/// <param name="name">Unique class name.</param>
		/// <param name="tree">Container tree object.</param>
		public Class(string name, Tree tree)
		{
			if(name == null || tree == null || name == "" || name == Tree.NODE_REGISTRY || name == Tree.CLASS_REGISTRY)
				return;
			this.name = name;
			this.tree = tree;
			renamed = false;
			data = tree.Data;
			// Check if exists
			if(data.Tables.IndexOf(name) >= 0)
			{
				// Loading an existing class
				id = uint.Parse((data.Tables[Tree.CLASS_REGISTRY].Select("name = '" + name + "'"))[0]["id"].ToString());
				columns = new SortedList();
				for(int i = 1; i < data.Tables[name].Columns.Count; i++)
				{
					columns.Add(data.Tables[name].Columns[i].ColumnName, (object) new Column(data.Tables[name].Columns[i].ColumnName, this));
				}
			}
			else
			{
				// Creates a new class
				id = 0;
				columns = new SortedList();
			}
		}

		/// <summary>
		/// Loads the Class by id.
		/// </summary>
		/// <param name="id">Unique class identifier.</param>
		/// <param name="tree">Container tree object.</param>
		public Class(uint id, Tree tree)
		{
			// Link to the tree
			this.tree = tree;
			data = tree.Data;
			renamed = false;
			// Load the class from DB
			DataRow[] rows = data.Tables[Tree.CLASS_REGISTRY].Select("id = " + id.ToString());
			if(rows.Length == 1)
			{
				this.id = id;
				name = rows[0]["name"].ToString();
				columns = new SortedList();
				for(int i = 1; i < data.Tables[name].Columns.Count; i++)
				{
					columns.Add(data.Tables[name].Columns[i].ColumnName, (object) new Column(data.Tables[name].Columns[i].ColumnName, this));
				}
			}
		}

		/// <summary>
		/// Unique class name.
		/// </summary>
		public string Name
		{
			get
			{
				return name;
			}
			set
			{
				// Check for if such name exists and perform the DB operations.
				DataRow[] rows = data.Tables[Tree.CLASS_REGISTRY].Select("name = '" + value + "'");
				if(rows.Length == 0 && value != Tree.CLASS_REGISTRY && value != Tree.NODE_REGISTRY)
				{
					oldName = name;
					name = value;
					renamed = true;
				}
			}
		}

		/// <summary>
		/// Unique class identifier.
		/// </summary>
		public uint Id
		{
			get
			{
				return id;
			}
		}

		/// <summary>
		/// Field definition for this class of nodes as field_name => data_type.
		/// </summary>
		public SortedList Columns
		{
			get
			{
				return columns;
			}
			set
			{
				// Check and perform the DB operations?
				columns = value;
			}
		}

		/// <summary>
		/// The tree this class belongs to.
		/// </summary>
		public Tree ParentTree
		{
			get
			{
				return tree;
			}
		}

		/// <summary>
		/// Registers the class in the dataset.
		/// </summary>
		/// <returns>True on success, false on error.</returns>
		public bool Add()
		{
			if(id == 0)
			{
				bool res = true;
				// Only new classes can be registered
				data.Tables.Add(name);
				// Node id column
				DataColumn col = new DataColumn();
				col.DataType = typeof(uint);
				// Add i18n support here
				col.Caption = "Node Id";
				col.ColumnName = Tree.NODE_ID;
				col.ReadOnly = true;
				col.AllowDBNull = false;
				col.Unique = true;
				data.Tables[name].Columns.Add(col);
				// User-defined columns
				for(int i = 0; i < columns.Count; i++)
				{
					res = res && ((Column) columns[i]).Add();
				}
				// Setting the primary key
				DataColumn[] keys = new DataColumn[1];
				keys[0] = data.Tables[name].Columns["id"];
				data.Tables[name].PrimaryKey = keys;
				// Registering the class in Tree.CLASS_REGISTRY
				DataRow row = data.Tables[Tree.CLASS_REGISTRY].NewRow();
				id = uint.Parse(row["id"].ToString());
				row["name"] = name;
				data.Tables[Tree.CLASS_REGISTRY].Rows.Add(row);
				// Applying changes
				data.AcceptChanges();
				renamed = false;
				tree.Modified = true;
				return true;
			}
			else
				return false;
		}

		/// <summary>
		/// Unregisters the class in the dataset.
		/// </summary>
		/// <param name="withNodes">Whether to remove all the nodes that belong to this class.</param>
		/// <returns>True on success, false on error.</returns>
		public bool Remove(bool withNodes)
		{
			if(id != 0)
			{
				// Only existing classes can be removed
				DataRow[] rows;
				// Removing the nodes if requested
				rows = data.Tables[Tree.NODE_REGISTRY].Select("cid=" + id.ToString());
				for(int i = 0; i < rows.Length; i++)
				{
					if(withNodes)
					{
						// Remove the nodes
						Node node = new Node(tree, uint.Parse(rows[i]["id"].ToString()));
						if(node.Id != 0)
							node.Delete();
					}
					else
					{
						// Set the cid to 0
						rows[i]["cid"] = 0;
						rows[i].AcceptChanges();
					}
				}
				// Removing the class iself
				if(data.Tables.CanRemove(data.Tables[name]))
				{
					data.Tables.Remove(name);
					// Removing from CLASS_REGISTRY
					rows = data.Tables[Tree.CLASS_REGISTRY].Select("id=" + id.ToString());
					if(rows.Length == 1)
					{
						rows[0].Delete();
						data.AcceptChanges();
						// Now remove from the tree
						tree.Classes.Remove(this.Name);
						tree.Modified = true;
						return true;
					}
					else
						return false;
				}
				else
					return false;
			}
			else
				return false;
		}

		/// <summary>
		/// Updates the class definition and data in dataset.
		/// </summary>
		/// <returns>True on success, false on error.</returns>
		public bool Update()
		{
			// Renaming if needed
			if(renamed)
			{
				DataRow[] rows = data.Tables[Tree.CLASS_REGISTRY].Select("id = " + id.ToString());
				if(rows.Length == 1)
				{
					data.Tables[rows[0]["name"].ToString()].TableName = name;
					rows[0]["name"] = name;
					// Change the key in the tree Classes
					tree.Classes.Remove(oldName);
					tree.Classes.Add(name, (object) this);
					renamed = false;
				}
				else
					return false;
			}
			// Accept the changes made to columns and the class name
			data.AcceptChanges();
			tree.Modified = true;
			return true;
		}

		/// <summary>
		/// Copies (inherits) another class.
		/// </summary>
		/// <param name="ancestor">Class to inherit.</param>
		public void Inherit(Class ancestor)
		{
			// Just copy all the columns
			for(int i = 0; i < ancestor.Columns.Count; i++)
			{
				string colName = ((Column) ancestor.Columns.GetByIndex(i)).Name;
				Column col = new Column(colName, this);
				col.Inherit((Column) ancestor.Columns.GetByIndex(i));
				col.Add();
			}
		}
		
		// TODO: implement table relations.
	}
}
